using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Collections;
using System.Drawing.Design;
using Aga.Controls.Tree.NodeControls;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Threading;
using Aga.Controls.Threading;


namespace Aga.Controls.Tree
{
	public partial class TreeViewAdv : Control
	{
		private const int LeftMargin = 7;
		internal const int ItemDragSensivity = 4;
		private readonly int _columnHeaderHeight;
		private const int DividerWidth = 9;
		private const int DividerCorrectionGap = -2;

		private Pen _linePen;
		private Pen _markPen;
		private bool _suspendUpdate;
		private bool _needFullUpdate;
		private bool _fireSelectionEvent;
		private NodePlusMinus _plusMinus;
		private Control _currentEditor;
		private EditableControl _currentEditorOwner;
		private ToolTip _toolTip;
		private DrawContext _measureContext;
		private TreeColumn _hotColumn = null;
		private IncrementalSearch _search;
		private List<TreeNodeAdv> _expandingNodes = new List<TreeNodeAdv>();
		private AbortableThreadPool _threadPool = new AbortableThreadPool();

		#region Public Events

		/// <summary>
		/// Occurs when [item drag].
		/// </summary>
		[Category("Action")]
		public event ItemDragEventHandler ItemDrag;
		/// <summary>
		/// Called when [item drag].
		/// </summary>
		/// <param name="buttons">The buttons.</param>
		/// <param name="item">The item.</param>
		protected virtual void OnItemDrag(MouseButtons buttons, object item)
		{
			if ( ItemDrag != null )
				ItemDrag(this, new ItemDragEventArgs(buttons, item));
		}

		[Category("Behavior")]
		public event EventHandler<TreeNodeAdvMouseEventArgs> NodeMouseClick;
		protected virtual void OnNodeMouseClick(TreeNodeAdvMouseEventArgs args)
		{
			if ( NodeMouseClick != null )
				NodeMouseClick(this, args);
		}

		[Category("Behavior")]
		public event EventHandler<TreeNodeAdvMouseEventArgs> NodeMouseDoubleClick;
		protected virtual void OnNodeMouseDoubleClick(TreeNodeAdvMouseEventArgs args)
		{
			if ( NodeMouseDoubleClick != null )
				NodeMouseDoubleClick(this, args);
		}

		[Category("Behavior")]
		public event EventHandler<TreeColumnEventArgs> ColumnWidthChanged;
		internal virtual void OnColumnWidthChanged(TreeColumn column)
		{
			if ( ColumnWidthChanged != null )
				ColumnWidthChanged(this, new TreeColumnEventArgs(column));
		}

		[Category("Behavior")]
		public event EventHandler<TreeColumnEventArgs> ColumnReordered;
		internal virtual void OnColumnReordered(TreeColumn column)
		{
			if ( ColumnReordered != null )
				ColumnReordered(this, new TreeColumnEventArgs(column));
		}

		[Category("Behavior")]
		public event EventHandler<TreeColumnEventArgs> ColumnClicked;
		internal virtual void OnColumnClicked(TreeColumn column)
		{
			if ( ColumnClicked != null )
				ColumnClicked(this, new TreeColumnEventArgs(column));
		}

		[Category("Behavior")]
		public event EventHandler SelectionChanged;
		internal virtual void OnSelectionChanged()
		{
			if ( SuspendSelectionEvent )
				_fireSelectionEvent = true;
			else {
				_fireSelectionEvent = false;
				if ( SelectionChanged != null )
					SelectionChanged(this, EventArgs.Empty);
			}
		}

		[Category("Behavior")]
		public event EventHandler<TreeViewAdvEventArgs> Collapsing;
		protected virtual void OnCollapsing(TreeNodeAdv node)
		{
			if ( Collapsing != null )
				Collapsing(this, new TreeViewAdvEventArgs(node));
		}

		[Category("Behavior")]
		public event EventHandler<TreeViewAdvEventArgs> Collapsed;
		protected virtual void OnCollapsed(TreeNodeAdv node)
		{
			if ( Collapsed != null )
				Collapsed(this, new TreeViewAdvEventArgs(node));
		}

		[Category("Behavior")]
		public event EventHandler<TreeViewAdvEventArgs> Expanding;
		protected virtual void OnExpanding(TreeNodeAdv node)
		{
			if ( Expanding != null )
				Expanding(this, new TreeViewAdvEventArgs(node));
		}

		[Category("Behavior")]
		public event EventHandler<TreeViewAdvEventArgs> Expanded;
		protected virtual void OnExpanded(TreeNodeAdv node)
		{
			EventHandler<TreeViewAdvEventArgs> tmp = this.Expanded;
			if ( tmp != null ) tmp(this, new TreeViewAdvEventArgs(node));
		}

		[Category("Behavior")]
		public event EventHandler GridLineStyleChanged;
		protected virtual void OnGridLineStyleChanged()
		{
			EventHandler tmp = this.GridLineStyleChanged;
			if ( tmp != null ) tmp(this, EventArgs.Empty);
		}

		[Category("Behavior")]
		public event EventHandler GridLineColorChanged;
		protected virtual void OnGridLineColorChanged()
		{
			EventHandler tmp = this.GridLineColorChanged;
			if ( tmp != null ) tmp(this, EventArgs.Empty);
		}
		#endregion

		/// <summary>
		/// Initializes a new instance of the <see cref="TreeViewAdv"/> class.
		/// </summary>
		public TreeViewAdv()
		{
			InitializeComponent();
			SetStyle(ControlStyles.AllPaintingInWmPaint
				| ControlStyles.UserPaint
				| ControlStyles.OptimizedDoubleBuffer
				| ControlStyles.ResizeRedraw
				| ControlStyles.Selectable
				, true);


			if ( Application.RenderWithVisualStyles )
				_columnHeaderHeight = 20;
			else
				_columnHeaderHeight = 17;

			//BorderStyle = BorderStyle.Fixed3D;
			_hScrollBar.Height = SystemInformation.HorizontalScrollBarHeight;
			_vScrollBar.Width = SystemInformation.VerticalScrollBarWidth;
			_rowLayout = new FixedRowHeightLayout(this, RowHeight);
			_rowMap = new List<TreeNodeAdv>();
			_selection = new List<TreeNodeAdv>();
			_readonlySelection = new ReadOnlyCollection<TreeNodeAdv>(_selection);
			_columns = new TreeColumnCollection(this);
			_toolTip = new ToolTip();

			_measureContext = new DrawContext();
			_measureContext.Font = Font;
			_measureContext.Graphics = Graphics.FromImage(new Bitmap(1, 1));

			Input = new NormalInputState(this);
			_search = new IncrementalSearch(this);
			CreateNodes();
			CreatePens();

			ArrangeControls();

			_plusMinus = new NodePlusMinus();
			_controls = new NodeControlsCollection(this);

			Font = _font;
			ExpandingIcon.IconChanged += ExpandingIconChanged;
		}

		/// <summary>
		/// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.Control"/> and its child controls and optionally releases the managed resources.
		/// </summary>
		/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
		protected override void Dispose(bool disposing)
		{
			if ( disposing && ( components != null ) ) {
				components.Dispose();
			}
			if ( disposing ) {
				// dispose members
				foreach ( var item in _columns ) {
					item.Dispose();
				}
				_columns.Clear();
				foreach ( var item in _controls ) {
					item.Dispose();
				}
				_controls.Clear();
				dispose(_dragTimer);
				dispose(_dragBitmap);
				dispose(_hotColumn);
				dispose(_hotControl);
				dispose(_hotNode);
				dispose(_hScrollBar);
				dispose(_vScrollBar);
				dispose(_innerCursor);
				dispose(_linePen);
				dispose(_markPen);
				dispose(_penGridLine);
			}
			// call base dispose
			base.Dispose(disposing);
		}
		private void dispose(object obj)
		{
			if ( obj == null ) return;
			try {
				IDisposable disposeObj = obj as IDisposable;
				if ( disposeObj != null ) {
					disposeObj.Dispose();
				}
			} catch {
			} finally {
				obj = null;
			}
		}

		private void ExpandingIconChanged(object sender, EventArgs e)
		{
			if ( IsHandleCreated )
				Invoke(new MethodInvoker(DrawIcons));
		}

		private void DrawIcons()
		{
			Graphics gr = Graphics.FromHwnd(this.Handle);
			int firstRowY = _rowLayout.GetRowBounds(FirstVisibleRow).Y;
			DrawContext context = new DrawContext();
			context.Graphics = gr;
			for ( int i = 0; i < _expandingNodes.Count; i++ ) {
				foreach ( NodeControlInfo info in GetNodeControls(_expandingNodes[i]) )
					if ( info.Control is ExpandingIcon ) {
						Rectangle rect = info.Bounds;
						rect.X -= OffsetX;
						rect.Y -= firstRowY;
						context.Bounds = rect;
						info.Control.Draw(info.Node, context);
					}
			}
			gr.Dispose();
		}

		#region Public Methods

		public TreePath GetPath(TreeNodeAdv node)
		{
			if ( node == _root )
				return TreePath.Empty;
			else {
				Stack<object> stack = new Stack<object>();
				while ( node != _root && node != null ) {
					stack.Push(node.Tag);
					node = node.Parent;
				}
				return new TreePath(stack.ToArray());
			}
		}

		public TreeNodeAdv GetNodeAt(Point point)
		{
			NodeControlInfo info = GetNodeControlInfoAt(point);
			return info.Node;
		}

		public NodeControlInfo GetNodeControlInfoAt(Point point)
		{
			if ( point.X < 0 || point.Y < 0 )
				return NodeControlInfo.Empty;

			int row = _rowLayout.GetRowAt(point);
			if ( row < RowCount && row >= 0 )
				return GetNodeControlInfoAt(RowMap[row], point);
			else
				return NodeControlInfo.Empty;
		}

		private NodeControlInfo GetNodeControlInfoAt(TreeNodeAdv node, Point point)
		{
			Rectangle rect = _rowLayout.GetRowBounds(FirstVisibleRow);
			point.Y += ( rect.Y - ColumnHeaderHeight );
			point.X += OffsetX;
			foreach ( NodeControlInfo info in GetNodeControls(node) )
				if ( info.Bounds.Contains(point) )
					return info;

			if ( FullRowSelect )
				return new NodeControlInfo(null, Rectangle.Empty, node);
			else
				return NodeControlInfo.Empty;
		}

		public void BeginUpdate()
		{
			_suspendUpdate = true;
			SuspendSelectionEvent = true;
		}

		public void EndUpdate()
		{
			_suspendUpdate = false;
			if ( _needFullUpdate )
				FullUpdate();
			else
				UpdateView();
			SuspendSelectionEvent = false;
		}

		public void ExpandAll()
		{
			_root.ExpandAll();
		}

		public void CollapseAll()
		{
			_root.CollapseAll();
		}

		/// <summary>
		/// Expand all parent nodes, and scroll to the specified node
		/// </summary>
		public void EnsureVisible(TreeNodeAdv node)
		{
			if ( node == null )
				throw new ArgumentNullException("node");

			if ( !IsMyNode(node) )
				throw new ArgumentException();

			if ( node != _root ) {
				TreeNodeAdv parent = node.Parent;
				while ( parent != _root ) {
					parent.IsExpanded = true;
					parent = parent.Parent;
				}
			}
			ScrollTo(node);
		}

		/// <summary>
		/// Make node visible, scroll if needed. All parent nodes of the specified node must be expanded
		/// </summary>
		/// <param name="node"></param>
		public void ScrollTo(TreeNodeAdv node)
		{
			if ( node == null )
				throw new ArgumentNullException("node");

			if ( !IsMyNode(node) )
				throw new ArgumentException();

			if ( node.Row < 0 )
				CreateRowMap();

			int row = -1;

			if ( node.Row < FirstVisibleRow )
				row = node.Row;
			else {
				int pageStart = _rowLayout.GetRowBounds(FirstVisibleRow).Top;
				int rowBottom = _rowLayout.GetRowBounds(node.Row).Bottom;
				if ( rowBottom > pageStart + DisplayRectangle.Height - ColumnHeaderHeight )
					row = _rowLayout.GetFirstRow(node.Row);
			}

			if ( row >= _vScrollBar.Minimum && row <= _vScrollBar.Maximum )
				_vScrollBar.Value = row;
		}

		public void ClearSelection()
		{
			BeginUpdate();
			try {
				ClearSelectionInternal();
			} finally {
				EndUpdate();
			}
		}

		internal void ClearSelectionInternal()
		{
			while ( Selection.Count > 0 )
				Selection[0].IsSelected = false;
		}

		#endregion

		protected override void OnSizeChanged(EventArgs e)
		{
			base.OnSizeChanged(e);
			performLayoutAdv();
		}

		protected override void OnLayout(LayoutEventArgs levent)
		{
			base.OnLayout(levent);
			performLayoutAdv();
		}
		
		private void performLayoutAdv()
		{
			ArrangeControls();
			SafeUpdateScrollBars();
		}

		private void ArrangeControls()
		{
			int hBarSize = _hScrollBar.Height;
			int vBarSize = _vScrollBar.Width;
			Rectangle clientRect = this.InternalClientRectangle;

			_hScrollBar.SetBounds(clientRect.X, clientRect.Bottom - hBarSize,
				clientRect.Width - vBarSize, hBarSize);

			_vScrollBar.SetBounds(clientRect.Right - vBarSize, clientRect.Y,
				vBarSize, clientRect.Height - hBarSize);
		}

		private void SafeUpdateScrollBars()
		{
			if ( InvokeRequired )
				Invoke(new MethodInvoker(UpdateScrollBars));
			else
				UpdateScrollBars();
		}

		private void UpdateScrollBars()
		{
			UpdateVScrollBar();
			UpdateHScrollBar();
			UpdateVScrollBar();
			UpdateHScrollBar();
			_hScrollBar.Width = DisplayRectangle.Width;
			_vScrollBar.Height = DisplayRectangle.Height;
		}

		private void UpdateHScrollBar()
		{
			_hScrollBar.Maximum = ContentWidth;
			_hScrollBar.LargeChange = Math.Max(DisplayRectangle.Width, 0);
			_hScrollBar.SmallChange = 5;
			_hScrollBar.Visible = _hScrollBar.LargeChange < _hScrollBar.Maximum;
			_hScrollBar.Value = Math.Min(_hScrollBar.Value, _hScrollBar.Maximum - _hScrollBar.LargeChange + 1);
		}

		private void UpdateVScrollBar()
		{
			if ( _rowLayout == null ) return;
			_vScrollBar.Maximum = Math.Max(RowCount - 1, 0);
			_vScrollBar.LargeChange = _rowLayout.PageRowCount;
			_vScrollBar.Visible = ( RowCount > 0 ) && ( _vScrollBar.LargeChange <= _vScrollBar.Maximum );
			_vScrollBar.Value = Math.Min(_vScrollBar.Value, _vScrollBar.Maximum - _vScrollBar.LargeChange + 1);
		}

		protected override CreateParams CreateParams
		{
			[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
			get
			{
				CreateParams res = base.CreateParams;
				//res.Style |= 0x07;
				//res.ExStyle &= ~0x200;
				//switch ( BorderStyle ) {
				//    case BorderStyle.FixedSingle:
				//        res.Style |= 0x800000;
				//        break;
				//    case BorderStyle.Fixed3D:
				//        res.ExStyle |= 0x200;
				//        break;
				//}
				return res;
			}
		}

		protected override void OnGotFocus(EventArgs e)
		{
			HideEditor();
			UpdateView();
			ChangeInput();
			base.OnGotFocus(e);
		}

		protected override void OnLeave(EventArgs e)
		{
			if ( _currentEditorOwner != null )
				_currentEditorOwner.ApplyChanges();
			HideEditor();
			UpdateView();
			base.OnLeave(e);
		}

		protected override void OnFontChanged(EventArgs e)
		{
			base.OnFontChanged(e);
			_measureContext.Font = Font;
			FullUpdate();
		}

		internal IEnumerable<NodeControlInfo> GetNodeControls(TreeNodeAdv node)
		{
			if ( node == null )
				yield break;
			Rectangle rowRect = _rowLayout.GetRowBounds(node.Row);
			foreach ( NodeControlInfo n in GetNodeControls(node, rowRect) )
				yield return n;
		}

		internal IEnumerable<NodeControlInfo> GetNodeControls(TreeNodeAdv node, Rectangle rowRect)
		{
			if ( node == null )
				yield break;

			int y = rowRect.Y;
			int x = ( node.Level - 1 ) * _indent + LeftMargin;
			int width = 0;
			Rectangle rect = Rectangle.Empty;

			if ( ShowPlusMinus ) {
				width = _plusMinus.GetActualSize(node, _measureContext).Width;
				rect = new Rectangle(x, y, width, rowRect.Height);
				if ( UseColumns && Columns.Count > 0 && Columns[0].Width < rect.Right )
					rect.Width = Columns[0].Width - x;

				yield return new NodeControlInfo(_plusMinus, rect, node);
				x += width;
			}

			if ( !UseColumns ) {
				foreach ( NodeControl c in NodeControls ) {
					Size s = c.GetActualSize(node, _measureContext);
					if ( !s.IsEmpty ) {
						width = s.Width;
						rect = new Rectangle(x, y, width, rowRect.Height);
						x += rect.Width;
						yield return new NodeControlInfo(c, rect, node);
					}
				}
			} else {
				int right = 0;
				foreach ( TreeColumn col in Columns ) {
					if ( col.IsVisible && col.Width > 0 ) {
						right += col.Width;
						for ( int i = 0; i < NodeControls.Count; i++ ) {
							NodeControl nc = NodeControls[i];
							if ( nc.ParentColumn == col ) {
								Size s = nc.GetActualSize(node, _measureContext);
								if ( !s.IsEmpty ) {
									bool isLastControl = true;
									for ( int k = i + 1; k < NodeControls.Count; k++ )
										if ( NodeControls[k].ParentColumn == col ) {
											isLastControl = false;
											break;
										}

									width = right - x;
									if ( !isLastControl )
										width = s.Width;
									int maxWidth = Math.Max(0, right - x);
									rect = new Rectangle(x, y, Math.Min(maxWidth, width), rowRect.Height);
									x += width;
									yield return new NodeControlInfo(nc, rect, node);
								}
							}
						}
						x = right;
					}
				}
			}
		}

		internal static double Dist(Point p1, Point p2)
		{
			return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
		}

		public void FullUpdate()
		{
			if ( InvokeRequired )
				Invoke(new MethodInvoker(UnsafeFullUpdate));
			else
				UnsafeFullUpdate();
		}

		private void UnsafeFullUpdate()
		{
			_rowLayout.ClearCache();
			CreateRowMap();
			SafeUpdateScrollBars();
			UpdateView();
			_needFullUpdate = false;
		}

		internal void UpdateView()
		{
			if ( !_suspendUpdate )
				Invalidate(false);
		}

		internal void UpdateHeaders()
		{
			Rectangle rect = this.DisplayRectangle;
			Invalidate(new Rectangle(rect.X, rect.Y, rect.Width, ColumnHeaderHeight));
		}

		internal void UpdateColumns()
		{
			FullUpdate();
		}

		private void CreateNodes()
		{
			Selection.Clear();
			SelectionStart = null;
			_root = new TreeNodeAdv(this, null);
			_root.IsExpanded = true;
			if ( _root.Nodes.Count > 0 )
				CurrentNode = _root.Nodes[0];
			else
				CurrentNode = null;
		}

		internal void ReadChilds(TreeNodeAdv parentNode)
		{
			ReadChilds(parentNode, false);
		}

		internal void ReadChilds(TreeNodeAdv parentNode, bool performFullUpdate)
		{
			if ( !parentNode.IsLeaf ) {
				parentNode.IsExpandedOnce = true;
				List<TreeNodeAdv> oldNodes = new List<TreeNodeAdv>(parentNode.Nodes);
				parentNode.Nodes.Clear();

				if ( Model != null ) {
					IEnumerable items = Model.GetChildren(GetPath(parentNode));
					if ( items != null )
						foreach ( object obj in items ) {
							bool found = false;
							if ( obj != null ) {
								for ( int i = 0; i < oldNodes.Count; i++ )
									if ( obj == oldNodes[i].Tag ) {
										oldNodes[i].RightBounds = oldNodes[i].Height = null;
										AddNode(parentNode, -1, oldNodes[i]);
										oldNodes.RemoveAt(i);
										found = true;
										break;
									}
							}
							if ( !found )
								AddNewNode(parentNode, obj, -1);

							if ( performFullUpdate )
								FullUpdate();
						}
				}

			}
		}

		private void AddNewNode(TreeNodeAdv parent, object tag, int index)
		{
			TreeNodeAdv node = new TreeNodeAdv(this, tag);
			AddNode(parent, index, node);
		}

		private void AddNode(TreeNodeAdv parent, int index, TreeNodeAdv node)
		{
			if ( index >= 0 && index < parent.Nodes.Count )
				parent.Nodes.Insert(index, node);
			else
				parent.Nodes.Add(node);

			node.IsLeaf = Model.IsLeaf(GetPath(node));
			if ( node.IsLeaf )
				node.Nodes.Clear();
			if ( !LoadOnDemand || node.IsExpandedOnce )
				ReadChilds(node);
		}

		private struct ExpandArgs
		{
			public TreeNodeAdv Node;
			public bool Value;
			public bool IgnoreChildren;
		}

		public void AbortBackgroundExpandingThreads()
		{
			_threadPool.CancelAll(true);
			for ( int i = 0; i < _expandingNodes.Count; i++ )
				_expandingNodes[i].IsExpandingNow = false;
			_expandingNodes.Clear();
			Invalidate();
		}

		internal void SetIsExpanded(TreeNodeAdv node, bool value, bool ignoreChildren)
		{
			ExpandArgs eargs = new ExpandArgs();
			eargs.Node = node;
			eargs.Value = value;
			eargs.IgnoreChildren = ignoreChildren;

			if ( AsyncExpanding && LoadOnDemand && !_threadPool.IsMyThread(Thread.CurrentThread) ) {
				WaitCallback wc = delegate(object argument) { SetIsExpanded((ExpandArgs)argument); };
				_threadPool.QueueUserWorkItem(wc, eargs);
			} else
				SetIsExpanded(eargs);
		}

		private void SetIsExpanded(ExpandArgs eargs)
		{
			bool update = !eargs.IgnoreChildren && !AsyncExpanding;
			if ( update )
				BeginUpdate();
			try {
				if ( IsMyNode(eargs.Node) && eargs.Node.IsExpanded != eargs.Value )
					SetIsExpanded(eargs.Node, eargs.Value);

				if ( !eargs.IgnoreChildren )
					SetIsExpandedRecursive(eargs.Node, eargs.Value);
			} finally {
				if ( update )
					EndUpdate();
			}
		}

		internal void SetIsExpanded(TreeNodeAdv node, bool value)
		{
			if ( Root == node && !value )
				return; //Can't collapse root node

			if ( value )
				OnExpanding(node);
			else
				OnCollapsing(node);

			if ( value && !node.IsExpandedOnce ) {
				if ( AsyncExpanding && LoadOnDemand ) {
					AddExpandingNode(node);
					node.AssignIsExpanded(true);
					Invalidate();
				}
				ReadChilds(node, AsyncExpanding);
				RemoveExpandingNode(node);
			}
			node.AssignIsExpanded(value);
			SmartFullUpdate();

			if ( value )
				OnExpanded(node);
			else
				OnCollapsed(node);
		}

		private void RemoveExpandingNode(TreeNodeAdv node)
		{
			node.IsExpandingNow = false;
			_expandingNodes.Remove(node);
		}

		private void AddExpandingNode(TreeNodeAdv node)
		{
			node.IsExpandingNow = true;
			_expandingNodes.Add(node);
			ExpandingIcon.Start();
		}

		internal void SetIsExpandedRecursive(TreeNodeAdv root, bool value)
		{
			for ( int i = 0; i < root.Nodes.Count; i++ ) {
				TreeNodeAdv node = root.Nodes[i];
				node.IsExpanded = value;
				SetIsExpandedRecursive(node, value);
			}
		}

		private void CreateRowMap()
		{
			RowMap.Clear();
			int row = 0;
			_contentWidth = 0;
			foreach ( TreeNodeAdv node in VisibleNodes ) {
				node.Row = row;
				RowMap.Add(node);
				if ( !UseColumns ) {
					_contentWidth = Math.Max(_contentWidth, GetNodeWidth(node));
				}
				row++;
			}
			if ( UseColumns ) {
				_contentWidth = 0;
				foreach ( TreeColumn col in _columns )
					if ( col.IsVisible )
						_contentWidth += col.Width;
			}
		}

		private int GetNodeWidth(TreeNodeAdv node)
		{
			if ( node.RightBounds == null ) {
				Rectangle res = GetNodeBounds(GetNodeControls(node, Rectangle.Empty));
				node.RightBounds = res.Right;
			}
			return node.RightBounds.Value;
		}

		internal Rectangle GetNodeBounds(TreeNodeAdv node)
		{
			return GetNodeBounds(GetNodeControls(node));
		}

		private Rectangle GetNodeBounds(IEnumerable<NodeControlInfo> nodeControls)
		{
			Rectangle res = Rectangle.Empty;
			foreach ( NodeControlInfo info in nodeControls ) {
				if ( res == Rectangle.Empty )
					res = info.Bounds;
				else
					res = Rectangle.Union(res, info.Bounds);
			}
			return res;
		}

		private void _vScrollBar_ValueChanged(object sender, EventArgs e)
		{
			FirstVisibleRow = _vScrollBar.Value;
		}

		private void _hScrollBar_ValueChanged(object sender, EventArgs e)
		{
			OffsetX = _hScrollBar.Value;
		}

		internal void SmartFullUpdate()
		{
			if ( _suspendUpdate )
				_needFullUpdate = true;
			else
				FullUpdate();
		}

		internal bool IsMyNode(TreeNodeAdv node)
		{
			if ( node == null )
				return false;

			if ( node.Tree != this )
				return false;

			while ( node.Parent != null )
				node = node.Parent;

			return node == _root;
		}

		private void UpdateSelection()
		{
			bool flag = false;

			if ( !IsMyNode(CurrentNode) )
				CurrentNode = null;
			if ( !IsMyNode(_selectionStart) )
				_selectionStart = null;

			for ( int i = Selection.Count - 1; i >= 0; i-- )
				if ( !IsMyNode(Selection[i]) ) {
					flag = true;
					Selection.RemoveAt(i);
				}

			if ( flag )
				OnSelectionChanged();
		}

		internal void ChangeColumnWidth(TreeColumn column)
		{
			if ( !( _input is ResizeColumnState ) ) {
				FullUpdate();
				OnColumnWidthChanged(column);
			}
		}

		public TreeNodeAdv FindNode(TreePath path)
		{
			return FindNode(path, false);
		}

		public TreeNodeAdv FindNode(TreePath path, bool readChilds)
		{
			if ( path.IsEmpty() )
				return _root;
			else
				return FindNode(_root, path, 0, readChilds);
		}

		private TreeNodeAdv FindNode(TreeNodeAdv root, TreePath path, int level, bool readChilds)
		{
			if ( !root.IsExpandedOnce && readChilds )
				ReadChilds(root);

			for ( int i = 0; i < root.Nodes.Count; i++ ) {
				TreeNodeAdv node = root.Nodes[i];
				if ( node.Tag == path.FullPath[level] ) {
					if ( level == path.FullPath.Length - 1 )
						return node;
					else
						return FindNode(node, path, level + 1, readChilds);
				}
			}
			return null;
		}

		public TreeNodeAdv FindNodeByTag(object tag)
		{
			return FindNodeByTag(_root, tag);
		}

		private TreeNodeAdv FindNodeByTag(TreeNodeAdv root, object tag)
		{
			foreach ( TreeNodeAdv node in root.Nodes ) {
				if ( node.Tag == tag )
					return node;
				TreeNodeAdv res = FindNodeByTag(node, tag);
				if ( res != null )
					return res;
			}
			return null;
		}

		public NodeControlInfo SelectedControl { get; set; }

		#region Editor

		public void DisplayEditor(Control control, EditableControl owner)
		{
			if ( control == null || owner == null )
				throw new ArgumentNullException();

			if ( CurrentNode != null ) {
				HideEditor();
				_currentEditor = control;
				_currentEditorOwner = owner;
				UpdateEditorBounds();

				UpdateView();
				control.Parent = this;
				control.Focus();
				owner.UpdateEditor(control);
			}
		}

		public void UpdateEditorBounds()
		{
			if ( _currentEditor != null ) {
				EditorContext context = new EditorContext();
				context.Owner = _currentEditorOwner;
				context.CurrentNode = CurrentNode;
				context.Editor = _currentEditor;
				context.DrawContext = _measureContext;

				SetEditorBounds(context);
			}
		}

		public void HideEditor()
		{
			if ( _currentEditorOwner != null ) {
				_currentEditorOwner.HideEditor(_currentEditor);
				_currentEditor = null;
				_currentEditorOwner = null;
			}
		}

		private void SetEditorBounds(EditorContext context)
		{
			foreach ( NodeControlInfo info in GetNodeControls(context.CurrentNode) ) {
				if ( context.Owner == info.Control && info.Control is EditableControl ) {
					Point p = info.Bounds.Location;
					p.X += info.Control.LeftMargin;
					p.X -= OffsetX;
					p.Y -= ( _rowLayout.GetRowBounds(FirstVisibleRow).Y - ColumnHeaderHeight );
					int width = DisplayRectangle.Width - p.X;
					if ( UseColumns && info.Control.ParentColumn != null && Columns.Contains(info.Control.ParentColumn) ) {
						Rectangle rect = GetColumnBounds(info.Control.ParentColumn.Index);
						width = rect.Right - OffsetX - p.X;
					}
					context.Bounds = new Rectangle(p.X, p.Y, width, info.Bounds.Height);
					( (EditableControl)info.Control ).SetEditorBounds(context);
					return;
				}
			}
		}

		private Rectangle GetColumnBounds(int column)
		{
			int x = 0;
			for ( int i = 0; i < Columns.Count; i++ ) {
				if ( Columns[i].IsVisible ) {
					if ( i < column )
						x += Columns[i].Width;
					else
						return new Rectangle(x, 0, Columns[i].Width, 0);
				}
			}
			return Rectangle.Empty;
		}

		#endregion

		#region ModelEvents
		private void BindModelEvents()
		{
			_model.NodesChanged += new EventHandler<TreeModelEventArgs>(_model_NodesChanged);
			_model.NodesInserted += new EventHandler<TreeModelEventArgs>(_model_NodesInserted);
			_model.NodesRemoved += new EventHandler<TreeModelEventArgs>(_model_NodesRemoved);
			_model.StructureChanged += new EventHandler<TreePathEventArgs>(_model_StructureChanged);
		}

		private void UnbindModelEvents()
		{
			_model.NodesChanged -= new EventHandler<TreeModelEventArgs>(_model_NodesChanged);
			_model.NodesInserted -= new EventHandler<TreeModelEventArgs>(_model_NodesInserted);
			_model.NodesRemoved -= new EventHandler<TreeModelEventArgs>(_model_NodesRemoved);
			_model.StructureChanged -= new EventHandler<TreePathEventArgs>(_model_StructureChanged);
		}

		private void _model_StructureChanged(object sender, TreePathEventArgs e)
		{
			if ( e.Path == null )
				throw new ArgumentNullException();

			TreeNodeAdv node = FindNode(e.Path);
			if ( node != null ) {
				ReadChilds(node);
				UpdateSelection();
				SmartFullUpdate();
			}
			//else 
			//	throw new ArgumentException("Path not found");
		}

		private void _model_NodesRemoved(object sender, TreeModelEventArgs e)
		{
			TreeNodeAdv parent = FindNode(e.Path);
			if ( parent != null ) {
				if ( e.Indices != null ) {
					List<int> list = new List<int>(e.Indices);
					list.Sort();
					for ( int n = list.Count - 1; n >= 0; n-- ) {
						int index = list[n];
						if ( index >= 0 && index <= parent.Nodes.Count )
							parent.Nodes.RemoveAt(index);
						else
							throw new ArgumentOutOfRangeException("Index out of range");
					}
				} else {
					for ( int i = parent.Nodes.Count - 1; i >= 0; i-- ) {
						for ( int n = 0; n < e.Children.Length; n++ )
							if ( parent.Nodes[i].Tag == e.Children[n] ) {
								parent.Nodes.RemoveAt(i);
								break;
							}
					}
				}
			}
			UpdateSelection();
			SmartFullUpdate();
		}

		private void _model_NodesInserted(object sender, TreeModelEventArgs e)
		{
			if ( e.Indices == null )
				throw new ArgumentNullException("Indices");

			TreeNodeAdv parent = FindNode(e.Path);
			if ( parent != null ) {
				for ( int i = 0; i < e.Children.Length; i++ )
					AddNewNode(parent, e.Children[i], e.Indices[i]);
			}
			SmartFullUpdate();
		}

		private void _model_NodesChanged(object sender, TreeModelEventArgs e)
		{
			TreeNodeAdv parent = FindNode(e.Path);
			if ( parent != null && parent.IsVisible && parent.IsExpanded ) {
				if ( InvokeRequired )
					Invoke(new UpdateContentWidthDelegate(ClearNodesSize), e, parent);
				else
					ClearNodesSize(e, parent);
				SmartFullUpdate();
			}
		}

		private delegate void UpdateContentWidthDelegate(TreeModelEventArgs e, TreeNodeAdv parent);
		private void ClearNodesSize(TreeModelEventArgs e, TreeNodeAdv parent)
		{
			if ( e.Indices != null ) {
				foreach ( int index in e.Indices ) {
					if ( index >= 0 && index < parent.Nodes.Count ) {
						TreeNodeAdv node = parent.Nodes[index];
						node.Height = node.RightBounds = null;
					} else
						throw new ArgumentOutOfRangeException("Index out of range");
				}
			} else {
				foreach ( TreeNodeAdv node in parent.Nodes ) {
					foreach ( object obj in e.Children )
						if ( node.Tag == obj ) {
							node.Height = node.RightBounds = null;
						}
				}
			}
		}
		#endregion
	}
}